#include "ReflectableVector.h"

#include "Reflection/Type.h"
#include <cstring>

CReflectableVector::CReflectableVector(CType *InDataType, const CReflectableVector &Other)
{
    ArrayNum=Other.ArrayNum;
    AllocatedNum=ArrayNum;

    if(AllocatedNum>0)
    {
        LowLevelData=malloc(AllocatedNum*InDataType->GetSize());
        for(uint64_t i=0;i<ArrayNum;i++)
        {
            InDataType->CloneInstance(Other.At(InDataType,i),At(InDataType,i));
        }
    }
    else
    {
        LowLevelData=nullptr;
    }
}

CReflectableVector::~CReflectableVector()
{
    assert(ArrayNum==0 && "you should call Clear() before destroy vector");

    if(LowLevelData)
    {
        free(LowLevelData);
    }
}

CReflectableVector &CReflectableVector::operator=(CReflectableVector &&Other)
{
    if(this!=&Other)
    {
        ArrayNum=Other.ArrayNum;
        AllocatedNum=Other.AllocatedNum;
        LowLevelData=Other.LowLevelData;

        Other.ArrayNum=0;
        Other.AllocatedNum=0;
        Other.LowLevelData=nullptr;
    }

    return *this;
}

void CReflectableVector::Reserve(CType *InDataType, uint64_t NewCapacity)
{
    if(NewCapacity>AllocatedNum)
    {
        //realloc will copy old data to new memory
        LowLevelData=realloc(LowLevelData,NewCapacity*InDataType->GetSize());
        AllocatedNum=NewCapacity;
    }
}

void CReflectableVector::ShrinkToFit(CType *InDataType)
{
    if(ArrayNum >= AllocatedNum)
    {
        return;
    }

    if(ArrayNum==0 )
    {
        free(LowLevelData);
        LowLevelData=nullptr;
        AllocatedNum=0;
    }
    else
    {
        if(ArrayNum!=AllocatedNum)
        {
            LowLevelData=realloc(LowLevelData,ArrayNum*InDataType->GetSize()); 
            AllocatedNum=ArrayNum;
        }
    }
}

void CReflectableVector::Resize(CType *InDataType, uint64_t NewSize, bool ShrinkCapacity)
{
    if(NewSize>ArrayNum)
    {
        Reserve(InDataType,NewSize);
        for(uint64_t i=ArrayNum;i<NewSize;i++)
        {
            InDataType->InitInstance(At(InDataType,i));
        }
    }
    else
    {
        for(uint64_t i=NewSize;i<ArrayNum;i++)
        {
            InDataType->EraseInstance(At(InDataType,i));
        }
    }

    ArrayNum=NewSize;

    if(ShrinkCapacity)
    {
        ShrinkToFit(InDataType);
    }

}

void CReflectableVector::Clear(CType *InDataType, bool ShrinkCapacity)
{
    for(uint64_t i=0;i<ArrayNum;i++)
    {
        InDataType->EraseInstance(At(InDataType,i));
    }

    ArrayNum=0;

    if(ShrinkCapacity)
    {
        free(LowLevelData);
        LowLevelData=nullptr;
        AllocatedNum=0;
    }
    
}

void CReflectableVector::AddDefault(CType *InDataType)
{
    Reserve(InDataType,ArrayNum+1);
    InDataType->InitInstance(At(InDataType,ArrayNum));
    ArrayNum++;
}

void CReflectableVector::Add(CType *InDataType, const void *InValue)
{
    Reserve(InDataType,ArrayNum+1);
    InDataType->CloneInstance(InValue,At(InDataType,ArrayNum));
    ArrayNum++;
}

void CReflectableVector::InsertDefault(CType *InDataType, uint64_t Index)
{
    Reserve(InDataType,ArrayNum+1);

    if(Index<ArrayNum)
    {
        memmove(At(InDataType,Index+1),At(InDataType,Index),(ArrayNum-Index)*InDataType->GetSize());
    }

    InDataType->InitInstance(At(InDataType,Index));

    ArrayNum++;
}

void CReflectableVector::Insert(CType *InDataType, uint64_t Index, const void *InValue)
{
    Reserve(InDataType,ArrayNum+1);

    if(Index<ArrayNum)
    {
        memmove(At(InDataType,Index+1),At(InDataType,Index),(ArrayNum-Index)*InDataType->GetSize());
    }

    InDataType->CloneInstance(InValue,At(InDataType,Index));

    ArrayNum++;
}

void CReflectableVector::RemoveAt(CType *InDataType, uint64_t Index, bool ShrinkCapacity)
{
    if(Index<ArrayNum)
    {
        InDataType->EraseInstance(At(InDataType,Index));
        memmove(At(InDataType,Index),At(InDataType,Index+1),(ArrayNum-Index-1)*InDataType->GetSize());
        ArrayNum--;
    }

    if(ShrinkCapacity)
    {
        ShrinkToFit(InDataType);
    }
}

void *CReflectableVector::At(CType *InDataType, uint64_t Index) const
{
    return (uint8_t*)LowLevelData+Index*InDataType->GetSize();
}

void CReflectableVector::SetAt(CType *InDataType, uint64_t Index, const void *InValue)
{
    InDataType->EraseInstance(At(InDataType,Index));
    InDataType->CloneInstance(InValue,At(InDataType,Index));
}

uint64_t CReflectableVector::GetHashCode(CType *InDataType) const
{
    uint64_t HashCode=0;
    for(uint64_t i=0;i<ArrayNum;i++)
    {
        HashCode^=InDataType->GetInstanceHash(At(InDataType,i));
    }
    return HashCode;
}

bool CReflectableVector::IsEqual(CType *InDataType, const CReflectableVector &Other) const
{
    if(ArrayNum!=Other.ArrayNum)
    {
        return false;
    }

    for(uint64_t i=0;i<ArrayNum;i++)
    {
        if(!InDataType->IsInstanceEqual(At(InDataType,i),Other.At(InDataType,i)))
        {
            return false;
        }
    }

    return true;
}

CReflectableVector::SDataAndSize CReflectableVector::GetDataAndSize(CType *InDataType) const
{
    SDataAndSize Result;
    Result.Data=LowLevelData;
    Result.Size=ArrayNum*InDataType->GetSize();
    return Result;
}

void CReflectableVector::SetDataAndSize(CType *InDataType, const void *InData, uint64_t InSize)
{
    uint64_t NewArrayNum=InSize/InDataType->GetSize();
    Reserve(InDataType,NewArrayNum);

    memcpy(LowLevelData,InData,InSize);

    ArrayNum=NewArrayNum;
}
